/*
  WritPlay (Sample Writ decoder)
  (c)2007 Roman Vorobets
*/
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ogg/ogg.h>
#include <writ/writ.h>

/*
  Basic UTF-8 manipulation routines
  by Jeff Bezanson
*/
static const long offsetsFromUTF8[6] =
{
  0x00000000UL, 0x00003080UL, 0x000E2080UL,
  0x03C82080UL, 0xFA082080UL, 0x82082080UL
};

static const char trailingBytesForUTF8[256] =
{
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

int u8_toucs(unsigned short *dest, int destsz, char *src, int srcsz)
{
  long ch;
  char *src_end = src + srcsz;
  int nb;
  int i = 0;

  while (i < destsz - 1)
  {
    nb = trailingBytesForUTF8[(unsigned char)*src];
    if (srcsz == -1)
    {
      if (*src == 0) break;
    } else
    {
      if (src + nb >= src_end) break;
    }
    ch = 0;
    switch (nb)
    {
    /* these fall through deliberately */
    case 3: ch += (unsigned char)*src++; ch <<= 6;
    case 2: ch += (unsigned char)*src++; ch <<= 6;
    case 1: ch += (unsigned char)*src++; ch <<= 6;
    case 0: ch += (unsigned char)*src++;
    }
    ch -= offsetsFromUTF8[nb];
    dest[i++] = (unsigned short)ch;
  }
  dest[i] = 0;
  return i;
}

void main(int argc, char *argv[])
{
  FILE *fin;
  ogg_sync_state oy;
  ogg_stream_state os;
  ogg_page og;
  ogg_packet op;
  writ_state ws;
  int stream_init = 0;

  setlocale(LC_ALL, "");
  printf(
    "OggWrit player\n"
    "(c)2007 Roman Vorobets\n\n"
    );
  if (2 != argc)
  {
    printf("usage: writ_play <infile.ogg>\n");
    exit(0);
  }
  fin = fopen(argv[1], "rb");
  ogg_sync_init(&oy);
  writ_decode_init(&ws);
  while (!feof(fin))
  {
    char *data = ogg_sync_buffer(&oy, 512);
    int nb_read = (int)fread(data, 1, 512, fin);
    ogg_sync_wrote(&oy, nb_read);
    while (1 == ogg_sync_pageout(&oy, &og))
    {
      if (!stream_init)
      {
        ogg_stream_init(&os, ogg_page_serialno(&og));
        ogg_stream_setdiscont(&os);
        stream_init = 1;
      }/* else
      if (os.serialno != ogg_page_serialno(&og))
      {
        ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
      }*/
      ogg_stream_pagein(&os, &og);
      while (1 == ogg_stream_packetout(&os, &op))
      {
        switch (stream_init)
        {
        case 1:
          if (1 == writ_decode_header(&ws, &op))
          {
            int i;
            printf("Writ information:\n");
            printf("* Version:      %d.%d\n", ws.wi->version, ws.wi->subversion);
            printf("* Rate:         %d/%d\n", ws.wi->granulerate_numerator, ws.wi->granulerate_denominator);
            printf("* Languages:    %d\n", ws.wi->num_languages);
            for (i = 0; i < ws.wi->num_languages; ++i)
              printf("  [%s] %s\n",
                ws.wi->languages[i].language_name,
                ws.wi->languages[i].language_desc);
            if (ws.wi->num_windows)
            {
              printf("* Window scale: %dx%d\n", ws.wi->location_scale_x, ws.wi->location_scale_y);
              printf("* Windows:    %d\n", ws.wi->num_windows);
              for (i = 0; i < ws.wi->num_windows; ++i)
              {
                writ_window *ww = ws.wi->windows + i;
                printf("  %2d. %dx%d at %d,%d (align: %d/%d)\n", 1 + i,
                  ww->location_width, ww->location_height,
                  ww->location_x, ww->location_y,
                  ww->alignment_x, ww->alignment_y);
              }
            }
            printf("\n");
            ++stream_init;
          }
        case 2:
          {
            ogg_int64_t start;
            ogg_int32_t duration;
            char *utf8_text;
            int window_id;
            if (!writ_decode_phrase(&ws, &op, &start, &duration, 0, &utf8_text, &window_id))
            {
              int istart = (int)start;
              int iend = (int)duration + istart;
              int time_s[4], time_e[4];
              wchar_t wide_text[257];
              char text[257];
              /* Rely on granulerate=1/1000 here */
              time_s[0] = istart / 3600000; istart %= 3600000;
              time_s[1] = istart /   60000; istart %=   60000;
              time_s[2] = istart /    1000; istart %=    1000;
              time_s[3] = istart;
              time_e[0] = iend / 3600000; iend %= 3600000;
              time_e[1] = iend /   60000; iend %=   60000;
              time_e[2] = iend /    1000; iend %=    1000;
              time_e[3] = iend;
              printf("%016I64X +++ %08X\n", start, duration);
              printf("%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n",
                time_s[0], time_s[1], time_s[2], time_s[3],
                time_e[0], time_e[1], time_e[2], time_e[3]);
              if (0 < ws.wi->num_windows)
                printf("### Window id: %d\n", window_id);
              u8_toucs((unsigned short*)wide_text, sizeof(wide_text) / sizeof(wide_text[0]),
                utf8_text, 1 + (int)strlen(utf8_text));
              wcstombs(text, wide_text, sizeof(text));
              printf("%s\n\n", text);
            }
          }
        }
      }
    }
  }
  writ_decode_clear(&ws);
  if (stream_init) ogg_stream_clear(&os);
  ogg_sync_clear(&oy);
  fclose(fin);
}
